#include "ExampleScene.h"

#include <graphics/utils.h>
#include <assert.h>
#include <math.h>

ExampleScene::ExampleScene()
    : Yam3DScene()
    , m_camera(0)
    , m_mesh1(0)
    , m_mesh2(0)
    , m_texture1(0)
    , m_texture2(0)
    , m_program(0)
    , m_totalTime(0.0f)
{
    // Create cool vertex and fragment shaders.
    QGLShader *vshader = new QGLShader(QGLShader::Vertex);
    char vShaderStr[] =
        "uniform mat4 matModelView;                                                   \n"
        "uniform mat4 matModelViewProj;                                               \n"
        "uniform mat4 matModelViewInverseTranspose;                                   \n"
        "attribute vec4 a_position;                                                   \n"
        "attribute vec3 a_normal;                                                     \n"
        "attribute vec2 a_texCoord;                                                   \n"
        "varying vec3 vNormalES;                                                      \n"
        "varying vec3 vViewVec;                                                       \n"
        "varying vec2 vTexCoord;                                                      \n"
        "void main()                                                                  \n"
        "{                                                                            \n"
        "   gl_Position = matModelViewProj * a_position;                              \n"
        "   vNormalES   = vec3(matModelViewInverseTranspose * vec4(a_normal, 0.0));   \n"
        "   vViewVec    = -vec3(matModelView * a_position);                           \n"
        "   vTexCoord    = a_texCoord;                           \n"
        "}                                                                            \n";
    vshader->compileSourceCode(vShaderStr);

    QGLShader *fshader = new QGLShader(QGLShader::Fragment);
    char fShaderStr[] =
        "uniform vec4  lightDir;                                                                                      \n"
        "varying vec3 vNormalES;                                                                                      \n"
        "varying vec2 vTexCoord;                                                                                      \n"
        "varying vec3 vViewVec;                                                                                       \n"
        "uniform sampler2D sTexture;                                                                                  \n"
        "void main()                                                                                                  \n"
        "{                                                                                                            \n"
        "    vec3 normal = normalize(vNormalES);                                                                      \n"
        "    // Soft diffuse                                                                                          \n"
        "    float diffuse = 0.5 + 0.5 * dot(lightDir.xyz, normal);                                                   \n"
        "    // Standard specular                                                                                     \n"
        "    float specular = pow(clamp(dot(reflect(-normalize(vViewVec), normal), lightDir.xyz),0.0, 1.0), 6.0);     \n"
        "                                                                                                             \n"
        "    gl_FragColor = texture2D( sTexture, vTexCoord ) * diffuse + vec4(0.9,0.9,0.9,1.0) * specular;           \n"
        "}                                                                                                            \n";
    fshader->compileSourceCode(fShaderStr);

    assert( m_program == 0 );
    m_program = new QGLShaderProgram();
    m_program->addShader(vshader);
    m_program->addShader(fshader);
    m_program->link();
    m_program->bind();

    // Create camera for our scene.
    assert( m_camera == 0 );
    m_camera = new graphics::Camera();
    m_camera->setTranslation( QVector3D(0,0,7.0f) );

    m_mesh1 = graphics::genPlane( 2, 2, 5.0f, 20.0f, 25.0f, 100.0f );
    m_mesh2 = graphics::genPlane( 2, 2, 5.0f, 20.0f, 25.0f, 100.0f );

    m_texture1 = graphics::createChessTexture(256,256,64,false);
    m_texture2 = graphics::createChessTexture(256,256,64,true);
}

ExampleScene::~ExampleScene()
{
}

void ExampleScene::resize(int width, int height)
{
    m_camera->setPerspective(60.0f, float(width)/float(height), 0.5f, 45.5f);
    glViewport( 0,0, width, height);
}

void ExampleScene::update(float deltaTime)
{
    m_totalTime += deltaTime;

    m_mesh1->setOrientation( QQuaternion::fromAxisAndAngle(1,0,0,-60) );
    m_mesh1->setTranslation( QVector3D(-3.6,-2.0f+0.6*sinf(m_totalTime*0.5f),0) );

    m_mesh2->setOrientation( QQuaternion::fromAxisAndAngle(-1,0,0,60) );
    m_mesh2->setTranslation( QVector3D(3.6,-2.0f+0.6*sinf(m_totalTime*0.5f),0) );
}

void ExampleScene::render()
{
    // Set render states
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    // Use the program object
    m_program->bind();

    // Light dir is in world space, convert it to camera space
    QVector4D lightDir =  m_camera->getView().inverted() * QVector4D(1.0f,1.0f,0.0f,0.0f);
    m_program->setUniformValue("lightDir", lightDir.normalized() );

    // Set the sampler texture unit to 0
    m_texture1->bind(0);

    // Override texture filtering
    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    m_program->setUniformValue("sTexture", 0 );

    // Render mesh (sets attributes and draws the object)
    m_mesh1->renderGeometryWithShader(m_program,m_camera);

    // Set the sampler texture unit to 0
    m_texture2->bind(0);

    // Override texture filtering
    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

    m_mesh2->renderGeometryWithShader(m_program,m_camera);
}


